package com.crazymaker.springcloud.standard.ratelimit;

import com.crazymaker.springcloud.common.distribute.rateLimit.RateLimitService;
import com.crazymaker.springcloud.standard.lua.ScriptHolder;
import com.crazymaker.springcloud.standard.properties.RedisRateLimitProperties;
import com.google.common.collect.ImmutableList;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class RedisRateLimitImpl implements RateLimitService, InitializingBean {

    private static final String RATE_LIMITER_KEY_PREFIX = "rate_limiter:";

    private static final String RATE_LIMITER_LUA_SHA_1 = RATE_LIMITER_KEY_PREFIX + "sha1";

    /**
     * redis 操作对象
     */
    private RedisTemplate redisTemplate;

    /**
     * redis 脚本实例
     */
    private RedisScript<Long> redisScript;

    private RedisRateLimitProperties limitProperties;

    private static final String redis_lua_script = "luaScript/rate_limiter.lua";

    private Map<String, LimiterInfo> limitMap = new HashMap<>();

    public RedisRateLimitImpl(RedisTemplate redisTemplate, RedisRateLimitProperties limitProperties) {
        this.redisTemplate = redisTemplate;
        this.limitProperties = limitProperties;
        // script内容
        String scriptContent = ScriptHolder.loadRateLimitLua(redis_lua_script);
        redisScript = new DefaultRedisScript<>(scriptContent, Long.class);
    }

    @Builder
    @Data
    public static class LimiterInfo {
        /**
         * 限流的类型，比如seckill
         */
        private String type;
        /**
         * 秒杀的ID
         */
        private String key;
        private Integer maxPermits;
        private Integer rate;
        public String fullKey() {
            return RATE_LIMITER_KEY_PREFIX + type + ":" + key;
        }
        public String cashKey() {
            return type + ":" + key;
        }
    }

    @Override
    public Boolean tryAcquire(String cacheKey) {
        // acquire, 1
        if (cacheKey == null)
            return false;
        if (cacheKey.indexOf(":") < 0) {
            cacheKey = "default:" + cacheKey;
        }
        LimiterInfo limiterInfo = limitMap.get(cacheKey);
        if (limiterInfo == null)
            return false;
        Long result = (Long) redisTemplate.execute(redisScript, ImmutableList.of(limiterInfo.fullKey()), "acquire", "1");
        if (result == 1) {
            return true;
        }
        return false;
    }

    @Override
    public void initLimitKey(String type, String key, Integer maxPermits, Integer rate) {
        Long result = (Long) redisTemplate.execute(redisScript, ImmutableList.of(key), "init",String.valueOf(maxPermits),String.valueOf(rate));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化map
        List<LimiterInfo> infoList = limitProperties.getLimiterInfoList();
        if (infoList == null || infoList.size() == 0)
            return;
        for (LimiterInfo info : infoList) {
            limitMap.put(info.cashKey(), info);
            initLimitKey(info.getType(), info.fullKey(), info.getMaxPermits(), info.getRate());
        }
    }



}
